﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading;
using System.Reflection;

using log4net;
using ConfigUtilLib;
using CommonLib;
using QueueMgrUtil;


namespace DataFetcherProcessorLib
{
    /// <summary>
    /// Purpose of this class is to Read Configuration 
    /// Start fetching data and adding to correct models 
    /// and adding a processing Job to the Queue
    /// Btw , this is singleton class 
    /// </summary>
    public class FetcherManager
    {
        private const string interfaceStr = "CommonLib.FetcherInterface"; 

        #region PRIVATE_MEMBERS_CONSTRUCTOR
        private static readonly log4net.ILog log = log4net.LogManager.GetLogger(System.Reflection.MethodBase.GetCurrentMethod().DeclaringType);
        private static BlockReadQueue<QueueWorkItem> _processingQueue = null;
        private static CountdownEvent _countdown=null;
        private static AppConfig _appConfig = null;
        #endregion 

        /// <summary>
        /// Constrtuctor : Init lot of good Stuff.. 
        /// Please follow Documentation inside the prodecure (a.k.a method  for JJJAAAVVVAAA Guys :-) ) 
        /// </summary>
        private  FetcherManager()
        {

        }

        public static void KickOffDataFetcher(AppConfig appCfg)
        {
           _processingQueue = QueueManager.WorkItemsQueue;
           _countdown = new CountdownEvent(appCfg.FetcherRecords.Count);
           _appConfig = appCfg;
           ThreadPool.QueueUserWorkItem(RunDatafetcherThread);
        }

        #region PRIVATE_UTILS_HELPERS

        private static void RunDatafetcherThread(Object args)
        {
            
           
            // Loop thru Configuration for all Frameworks and Fetch Data 
            foreach ( FetcherConfigBaseRec  fetcherRec in _appConfig.FetcherRecords)
            {                
                if (!ThreadPool.QueueUserWorkItem(GoFetchData, fetcherRec))
                {
                    Console.Error.WriteLine("Failed to Queue Fetcher to Thread.. {0}", fetcherRec.FetcherName);
                }
                else
                {
                    log.DebugFormat("Queued to Fetcher Thread.. :  {0}", fetcherRec.FetcherName);
                }
                
            }

            // Queue End of the work Queue Item 
            _countdown.Wait();
            QueueWorkItem endoftheWork = new QueueWorkItem(QUEUE_WORK_ITEM_TYPE.PROCESS_END, null, null);
            _processingQueue.Enqueue(endoftheWork);

        }


        
        private static void  GoFetchData( object fetchRecordObject )
        {

            FetcherConfigBaseRec fetchRec = (FetcherConfigBaseRec)fetchRecordObject;            
            log.DebugFormat(" Fetching Data for  fetch Record :" + fetchRec.FetcherName);

            //  perform Fecthing Operation 

            if (_processingQueue != null)
            {
                Object rawDataFromCrawler = null;
                Assembly reflectionAssembly = null;
                FetcherBin fetcherBinRec = _appConfig.appgeneralConfig.getFeatureBin(fetchRec.FetcherType);

                if( fetcherBinRec ==  null )
                {
                    return;
                }

                try
                {
                    // 1. Load Library 
                    reflectionAssembly = Assembly.LoadFrom(fetcherBinRec.Dll);
                }
                catch (Exception e)
                {
                    log.DebugFormat("Unable to Load DLL : {0} : {1}", fetcherBinRec.Dll, e.Message);
                    return ;
                }

                if (reflectionAssembly == null) return;

                foreach (Type classType in reflectionAssembly.GetTypes())
                {
                    if ((classType == null) || (classType.IsClass != true) || (classType.GetInterface(interfaceStr) == null))
                    {
                        continue;
                    }
                    // if all good Invoke The method 
                    FetcherInterface ibaseObject = Activator.CreateInstance(classType) as FetcherInterface;
                    log.DebugFormat("......Dynamically Invoking compute() on {0} ", classType.FullName);
                    // Kick Off Crawling /Fetching data  
                    rawDataFromCrawler = ibaseObject.crawlForData(fetchRec);
                    break;  //  Let us do for only no class object 
                }

               
                

                QueueWorkItem workItemToProcess = new QueueWorkItem(QUEUE_WORK_ITEM_TYPE.PROCESS_WORK_ITEM, fetchRec, rawDataFromCrawler );
                _processingQueue.Enqueue(workItemToProcess);
                // Signal Back 
                _countdown.Signal();
            }
            
        }



        #endregion 
    }
}
